package daos

import (
	"bsm/logger"
	"bsm/models"
	"database/sql"
	"errors"
	"fmt"
	"strings"

	"github.com/jmoiron/sqlx"
)

type ImportDao interface {
	Reset()
	// 开启一个工作单元（事务）
	Begin() error
	// 提交/回滚
	Commit() error
	Rollback() error
	InTransaction(fn func() error) error

	GetOrgByProjectCode(prjcode string) (models.Org, error)
	GetUserIdByName(orgcode string, name string) (string, error)
	ImportBase(project models.ImportBase, uid string) error
	ImportLabor(data []models.ImportLaborItem) error
	ImportMaterial(items []models.ImportMaterialItem) error
	ImportOther(items []models.ImportOtherItem) error
	CheckProjectExit(prjcode string) (bool, error)
	GetProjectState(prjcode string) (int, error)
	SetProjectState(prjcode string, state int) error
}

type importDaoImpl struct {
	dbImport *sqlx.DB
	txImport *sqlx.Tx
}

func NewImportDao(dbImport *sqlx.DB) ImportDao {
	return &importDaoImpl{
		dbImport: dbImport,
		txImport: nil,
	}
}

func (dao *importDaoImpl) Reset() {
	if dao.txImport != nil {
		dao.Rollback() // 尝试回滚未完成的事务
	}
	dao.txImport = nil // 确保状态清理
}

func (dao *importDaoImpl) Begin() error {
	if dao.txImport != nil {
		logger.Warnf("Transaction already exists")
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.Begin",
		}).Warnf("Transaction already exists")
		return errors.New("事务已存在")
	}
	tx, err := dao.dbImport.Beginx()
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.Begin",
		}).Errorf("failed to begin import transaction: %v", err)
		return err
	}
	dao.txImport = tx
	return nil
}

func (dao *importDaoImpl) Commit() error {
	if dao.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.Commit",
		}).Warnf("No active transaction to commit")
		return errors.New("没有活动的事务")
	}
	err := dao.txImport.Commit()
	dao.txImport = nil // 关键修复：提交后清理事务
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.Commit",
		}).Errorf("failed to commit import transaction: %v", err)
		return fmt.Errorf("failed to commit import transaction: %w", err)
	}
	return nil
}

func (dao *importDaoImpl) Rollback() error {
	if dao.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.Rollback",
		}).Warnf("No active transaction to rollback")
		return errors.New("没有活动的事务")
	}
	err := dao.txImport.Rollback()
	dao.txImport = nil // 关键修复：回滚后清理事务
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.Rollback",
		}).Errorf("rollback errors: import=%v", err)
		return fmt.Errorf("rollback errors: import=%w", err)
	}
	return nil
}

// 事务模板方法（推荐）
func (d *importDaoImpl) InTransaction(fn func() error) error {
	if d.txImport != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.InTransaction",
		}).Warnf("Transaction already exists, rolling back existing transaction")
		d.Rollback() // 清理已有事务
		return errors.New("事务已存在")
	}

	if err := d.Begin(); err != nil {
		return err
	}

	defer func() {
		if r := recover(); r != nil {
			d.Rollback()
			logger.WithFields(map[string]interface{}{
				"layer":     "dao",
				"operation": "importDao.InTransaction",
			}).Errorf("事务因panic回滚: %v", r)
		}
	}()

	if err := fn(); err != nil {
		d.Rollback()
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.InTransaction",
		}).Errorf("transaction failed: %v", err)
		return err
	}

	return d.Commit()
}

func (id *importDaoImpl) GetUserIdByName(orgcode string, name string) (string, error) {
	var uid string
	err := id.dbImport.QueryRow("SELECT user_id FROM bsm_user WHERE org_code=? and user_name=? ", orgcode, name).Scan(&uid)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.GetUserIdByName",
		}).Errorf("GetUserIdByName failed - orgcode: %s, name: %s, error: %v", orgcode, name, err)
		return "", err
	}
	return uid, nil
}

func (id *importDaoImpl) GetOrgByProjectCode(prjcode string) (models.Org, error) {
	var org models.Org
	var prefix string
	if pos := strings.Index(prjcode, "-"); pos != -1 {
		prefix = prjcode[:pos+len("-")]
	} else {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.GetOrgCodeByProjectCode",
		}).Errorf("项目编码格式错误 - prjcode: %s", prjcode)
		return org, fmt.Errorf("项目编码格式错误")
	}

	err := id.dbImport.QueryRow("SELECT org_name,org_code,org_level FROM bsm_org WHERE prefix=?", prefix).Scan(&org.OrgName, &org.OrgCode, &org.OrgLevel)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.GetOrgCodeByProjectCode",
		}).Errorf("GetOrgCodeByProjectCode failed - prjcode: %s, error: %v", prjcode, err)
		return org, err
	}
	return org, nil
}

func (id *importDaoImpl) ImportBase(project models.ImportBase, uid string) error {
	if id.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importBase",
		}).Errorf("importBase 必须在事务中调用")
		return errors.New("importBase 必须在事务中调用")
	}
	project.Importer = uid
	project.Imported = 1

	query := "INSERT INTO project_base (name, design_code, construction_unit,project_state,org_code,designer, reviewer, preparation_date,initiator, project_type, business_type,construction_type,project_overview, technical_analysis,profit_statement,point_old,point_new,point_cost,recycle_cycle,total_cost,time_budget,time_settle,time_audit,importer,imported) VALUES (:name,:design_code, :construction_unit,:project_state, :org_code, :designer, :reviewer, :preparation_date, :initiator, :project_type, :business_type, :construction_type, :project_overview, :technical_analysis, :profit_statement,:point_old,:point_new,:point_cost,:recycle_cycle,:total_cost,:time_budget, :time_settle, :time_audit, :importer,:imported)"
	_, err := id.dbImport.NamedExec(query, project)

	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importBase",
		}).Errorf("importBase failed - project: %v, error: %v", project, err)
		return err
	}
	return nil
}

func (id *importDaoImpl) ImportLabor(data []models.ImportLaborItem) error {
	if len(data) == 0 {
		return nil
	}
	if id.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importLabor",
		}).Errorf("importLabor 必须在事务中调用")
		return errors.New("importLabor 必须在事务中调用")
	}
	_, err := id.dbImport.NamedExec("INSERT INTO project_labor (lid,design_code, design_type, code, quantity) VALUES (:lid,:design_code, :design_type, :code, :quantity)", data)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importLabor",
		}).Errorf("importLabor failed - data: %v, error: %v", data, err)
		return err
	}
	return nil
}

func (id *importDaoImpl) ImportMaterial(data []models.ImportMaterialItem) error {
	if len(data) == 0 {
		return nil
	}
	if id.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importMaterial",
		}).Errorf("importMaterial 必须在事务中调用")
		return errors.New("importMaterial 必须在事务中调用")
	}
	_, err := id.dbImport.NamedExec("INSERT INTO project_material (design_code, design_type, code, price, quantity) VALUES (:design_code, :design_type, :code, :price, :quantity)", data)

	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importMaterial",
		}).Errorf("importMaterial failed - data: %v, error: %v", data, err)
		return err
	}
	return nil
}

func (id *importDaoImpl) ImportOther(data []models.ImportOtherItem) error {
	if len(data) == 0 {
		return nil
	}
	if id.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importOther",
		}).Errorf("importOther 必须在事务中调用")
		return errors.New("importOther 必须在事务中调用")
	}
	_, err := id.dbImport.NamedExec("INSERT INTO project_other (design_code, design_type, serial_number, name, basis, tax_excluded, tax, tax_included,remark) VALUES (:design_code, :design_type, :serial_number, :name, :basis, :tax_excluded, :tax, :tax_included,:remark)", data)

	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.importOther",
		}).Errorf("importOther failed - data: %v, error: %v", data, err)
		return err
	}
	return nil
}

func (id *importDaoImpl) CheckProjectExit(prjcode string) (bool, error) {
	var count int
	err := id.dbImport.QueryRow("SELECT COUNT(*) FROM project_base WHERE design_code = ?", prjcode).Scan(&count)
	if err != nil {
		if err == sql.ErrNoRows {
			count = 0
		} else {
			logger.WithFields(map[string]interface{}{
				"layer":     "dao",
				"operation": "importDao.CheckBudgetExit",
			}).Errorf("CheckBudgetExit failed - prjcode: %s, error: %v", prjcode, err)
			return false, err
		}
	}
	return count > 0, nil
}

func (id *importDaoImpl) GetProjectState(prjcode string) (int, error) {
	var state int
	err := id.dbImport.QueryRow("SELECT project_state FROM project_base WHERE design_code = ?", prjcode).Scan(&state)
	if err != nil {
		if err == sql.ErrNoRows {
			state = 0
		} else {
			logger.WithFields(map[string]interface{}{
				"layer":     "dao",
				"operation": "importDao.GetProjectState",
			}).Errorf("GetProjectState failed - prjcode: %s, error: %v", prjcode, err)
			return 0, err
		}
	}
	return state, nil
}

func (id *importDaoImpl) SetProjectState(prjcode string, state int) error {
	if id.txImport == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.SetProjectState",
		}).Errorf("SetProjectState 必须在事务中调用")
		return errors.New("SetProjectState 必须在事务中调用")
	}
	_, err := id.dbImport.Exec("UPDATE project_base SET project_state = ? WHERE design_code = ?", state, prjcode)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "dao",
			"operation": "importDao.SetProjectState",
		}).Errorf("SetProjectState failed - prjcode: %s, state: %d, error: %v", prjcode, state, err)
		return err
	}
	return nil
}
